package core.proto;

import java.awt.Adjustable;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FontMetrics;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.AdjustmentEvent;
import java.awt.event.AdjustmentListener;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JDesktopPane;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Highlighter;
import javax.swing.text.Highlighter.Highlight;
import javax.swing.event.CaretEvent;
import javax.swing.event.CaretListener;

import org.apache.commons.io.FilenameUtils;

import core.proto.annotation.Annotation;
import core.proto.annotation.AnnotationManager;
import core.proto.annotation.AnnotationModel.AnnotationLocation;
import core.proto.annotation.CannotAddAnnotationException;
import core.proto.annotation.DocAnnotationModel;
import core.proto.annotation.DocAnnotationUI;
import core.proto.rightwindow.BoardPane;
import core.util.logging.UserLogWriter;
import core.util.logging.constants.Action;
import core.util.logging.constants.Location;

/**
 * This class handles highlighting of text in doc by listening to the caret change.  
 */
public class HighlightListener implements CaretListener {


	static final int FRAME_SIZE = 200;

	static ImageIcon hilightIcon;

	static MouseAdapter adapter; 

	PaintDoc doc;
	JLayeredPane layeredPane;
	Toggle toggle;

	static {
		hilightIcon = new ImageIcon(Util.getServerResource("resoures/img/highlighter.png"));

		adapter = new MouseAdapter(){
			public void mouseClicked(MouseEvent me) {
				Toggle button = (Toggle) me.getSource();
				button.flip();
				String status = "User has turned " + (button.isHiliting ? "ON" : "OFF") + " highlighting";
				UserLogWriter.log2(status, Location.Documents, Action.CLICK);
			}
		};
	}


	public JButton createToggle () {
		return toggle;
	}

	public HighlightListener(PaintDoc doc, JLayeredPane pane){
		this.doc = doc;
		this.layeredPane = pane;

		toggle = new Toggle();
		toggle.setIcon(hilightIcon);
		toggle.addMouseListener(adapter);		
	}


	private boolean overlaps(int start1, int end1, int start2, int end2){
		return (start1 <= start2 && start2 <= end1) ||
				(start1 <= end2 && end2 <= end1) ||
				(start2 <= start1 && start1 <= end2) ||
				(start2 <= end1 && end1 <= end2);
	}

	private String getDocName(){
		return FilenameUtils.getBaseName(doc.docKey);
	}


	/**
	 * Returns true if remove, false if will remove. 
	 * @return
	 */
	private boolean decideRemovalOfAnnoTiedToHilite(){
		final String prompt = "The highlight is associated with an annotation. Really continue?"; 
		JOptionPane pane = new JOptionPane(prompt);
		Object[] options = new String[] { "Yes", "No" };
		pane.setOptions(options);

		JDialog dialog = pane.createDialog(new JFrame(), "Dilaog");		
		dialog.setVisible(true);

		Object obj = pane.getValue();
		int result = -1;

		for (int k = 0; k < options.length; k++)
			if (options[k].equals(obj))
				result = k;
		return result == 0;
	}

	public void caretUpdate(final CaretEvent event) {
		/**
		 * Invariant: no two intervals i != j s.t. i, j overlaps. 
		 */
		int start = Math.min(event.getDot(), event.getMark());
		int end = Math.max(event.getDot(), event.getMark());
		
		if (! BoardPane.getInstance().canCreateLocalPostIt()) {
			return; 
		}
		
		if (start != end && toggle.isHiliting){
			Highlight[] hilites = doc.getHighlighter().getHighlights();
			try{
				List<Highlight> overlapping;
				for(Highlight hilite : hilites){
					if (hilite.getPainter() == doc.userHighlightPainter){
						// Removing existing highlights since user dragged in region of one.  
						if (hilite.getStartOffset() <= start && hilite.getEndOffset() >= end){

							// This is definitely more of a beta feature now since we haven't worked out how the "warning" 
							// should look like. I suspect it's going to be more of an annoyance than feature for users.
							boolean annotationChanged = false;
							for (Annotation anno : AnnotationManager.getAnnotations(getDocName(), AnnotationLocation.DOC)){

								DocAnnotationModel m = (DocAnnotationModel) anno.getModel();

								// Find the annotation corresponding to this hilite we want to alter. 
								if (m.getStartIndex() == hilite.getStartOffset() && m.getEndIndex() == hilite.getEndOffset()){

									if (! anno.getView().isEditing() || ! m.getUserText().equals("")){
										if (! decideRemovalOfAnnoTiedToHilite()){
											// No removing. 
											return;
										} else {
											// Erase the annotation from view. 
											anno.getView().removeFromPane();
											m.setDelete(true);
											annotationChanged = true;
										}
									}
								}
							}
							if (annotationChanged)
								AnnotationManager.writeFile(getDocName()); // Save each time annotation for this doc is updated.

							doc.getHighlighter().removeHighlight(hilite);
							// Add back in the remaining highlight section. 
							if (hilite.getStartOffset() < start){
								doc.getHighlighter().addHighlight(hilite.getStartOffset(), start, doc.userHighlightPainter);
							}
							if (end < hilite.getEndOffset()){
								doc.getHighlighter().addHighlight(end, hilite.getEndOffset(), doc.userHighlightPainter);
							}
							HighlightManager.updateHighlights(doc.docKey, doc.getHighlighter().getHighlights(), doc.userHighlightPainter);
							// Done at this point since we removed some hilites. 
							return; 
						}
					}
				}
				// Join together all intervals that overlaps with the one we are about to add. 
				// This keeps the invariant that there are no overlapping intervals. 
				overlapping = new LinkedList<Highlight>();
				for(Highlight hilite : hilites){
					if (overlaps(hilite.getStartOffset(), hilite.getEndOffset(), start, end)){
						overlapping.add(hilite);
						doc.getHighlighter().removeHighlight(hilite);
					}
				}
				// Sort and merge overlapping intervals. 
				if (overlapping.size() > 0){
					Collections.sort(overlapping, new Comparator<Highlight>(){
						public int compare(Highlight left, Highlight right){
							if (left.getStartOffset() == right.getStartOffset())
								return 0;
							return left.getStartOffset() < right.getStartOffset() ? -1 : 1;
						}
					});
					// figure out the endpoints of the new, conjoined interval.
					start = Math.min(start, overlapping.get(0).getStartOffset());
					end = Math.max(end, overlapping.get(overlapping.size()-1).getEndOffset());
				}
				// Add the new highlight. 
				doc.getHighlighter().addHighlight(start, end, doc.userHighlightPainter);
				HighlightManager.updateHighlights(doc.docKey, doc.getHighlighter().getHighlights(), doc.userHighlightPainter);

				// Prompt user to enter annotation text. 
				String selectedText = doc.getDocument().getText(start, end - start);
				Point annotationPos = doc.getCaret().getMagicCaretPosition();
				// Always just add to the left margin of the document for now.

				annotationPos.setLocation(0, doc.getCaret().getMagicCaretPosition().getY());

				Point pos = doc.getCaret().getMagicCaretPosition();


				DocAnnotationModel m = new DocAnnotationModel(start, end, selectedText, "", getDocName(), pos.getX(), pos.getY());
				DocAnnotationUI ui;
				try {
					ui = new DocAnnotationUI(m);
					Annotation annotation = new Annotation(m, ui, false);
					annotation.getView().show(pos.getX(), pos.getY());
					annotation.getView().addToPane(layeredPane);
				} catch (CannotAddAnnotationException e) {
					// Show popup. 
					Util.showCannotAddAnnotationPopup (layeredPane);
				}
			}catch(BadLocationException ble){
				System.err.println("Bad location");
			}
		}
	}

	private class Toggle extends JButton {
		public boolean isHiliting = false;

		public Toggle(){			
			setText(getButtonDisplayMessage());
		}

		public void flip(){
			isHiliting = !isHiliting;
			setText(getButtonDisplayMessage());
		}

		private String getButtonDisplayMessage() {
			return "Highlighting is " + (isHiliting? "ON" : "OFF");
		}
	}
}
